package org.clear.client.http;

import java.io.BufferedReader;
import java.io.File;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;

public class HttpUtils {

	public static class HttpResult {
		public int code;
		public String content;
		public Exception exception;
		
		public boolean successful(){
			return (code < 400 && code > 199);
		}
	}

	private static class CountingMultipartRequestEntity implements HttpEntity {
		private HttpEntity delegate_;
		private ProgressListener listener_;

		public CountingMultipartRequestEntity(HttpEntity delegate,
				ProgressListener listener) {
			super();
			delegate_ = delegate;
			listener_ = listener;
		}

		public void consumeContent() throws IOException {
			delegate_.consumeContent();
		}

		public InputStream getContent() throws IOException,
				IllegalStateException {
			return delegate_.getContent();
		}

		public Header getContentEncoding() {
			return delegate_.getContentEncoding();
		}

		public long getContentLength() {
			return delegate_.getContentLength();
		}

		public Header getContentType() {
			return delegate_.getContentType();
		}

		public boolean isChunked() {
			return delegate_.isChunked();
		}

		public boolean isRepeatable() {
			return delegate_.isRepeatable();
		}

		public boolean isStreaming() {
			return delegate_.isStreaming();
		}

		public void writeTo(OutputStream outstream) throws IOException {
			delegate_.writeTo(new CountingOutputStream(outstream, listener_));
		}

		private class CountingOutputStream extends FilterOutputStream {

			private final ProgressListener listener;

			private long transferred;

			public CountingOutputStream(final OutputStream out,
					final ProgressListener listener) {
				super(out);
				this.listener = listener;
				this.transferred = 0;
			}

			public void write(byte[] b, int off, int len) throws IOException {
				out.write(b, off, len);
				this.transferred += len;
				if (listener != null)
					this.listener.transferred(this.transferred);
			}

			public void write(int b) throws IOException {
				out.write(b);
				this.transferred++;
				if (listener != null)
					this.listener.transferred(this.transferred);
			}
		}
	}

	private static class HttpTask implements Runnable {
		private HttpCallback callback_;
		private HttpResponse response_;
		private Exception exception_;
		private Map<String, Object> postData_;
		private String url_;
		private ProgressListener listener_;

		public HttpTask(String url, HttpCallback callback_) {
			super();
			url_ = url;
			this.callback_ = callback_;
		}

		public ProgressListener getListener() {
			return listener_;
		}

		public void setListener(ProgressListener listener) {
			listener_ = listener;
		}

		public Map<String, Object> getPostData() {
			return postData_;
		}

		public void setPostData(Map<String, Object> postData) {
			postData_ = postData;
		}

		public void doHttp(String url) {
			try {
				HttpClient client = new DefaultHttpClient();

				if (postData_ == null) {
					HttpGet request = new HttpGet(url);
					response_ = client.execute(request);
				} else {
					HttpPost request = new HttpPost(url);

					boolean multipart = false;
					for (String param : postData_.keySet()) {
						if (postData_.get(param) instanceof File) {
							multipart = true;
						}
					}

					if (multipart) {
//						MultipartEntity entity = new MultipartEntity();
//						for (String param : postData_.keySet()) {
//							if (postData_.get(param) instanceof File) {
//								entity.addPart(param, new FileBody(
//										(File) postData_.get(param)));
//							} else {
//								entity.addPart(param, new StringBody(""
//										+ postData_.get(param)));
//							}
//						}
//						request.setEntity(new CountingMultipartRequestEntity(
//								entity, listener_));
					} else {
						List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
						for (String param : postData_.keySet()) {
							nameValuePairs.add(new BasicNameValuePair(param, ""
									+ postData_.get(param)));
						}
						request.setEntity(new UrlEncodedFormEntity(
								nameValuePairs));
					}

					response_ = client.execute(request);
				}

			} catch (Exception e) {
				exception_ = e;
			}

			if (callback_ != null) {
				if (exception_ != null)
					callback_.onError(exception_);
				else
					callback_.onResponse(response_);
			}
		}

		public void run() {
			doHttp(url_);
		}
	}

	public static String convertStreamToString(InputStream is) {

		BufferedReader reader = new BufferedReader(new InputStreamReader(is));
		StringBuilder sb = new StringBuilder();

		String line = null;
		try {
			while ((line = reader.readLine()) != null) {
				sb.append(line + "\n");
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				is.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return sb.toString();
	}

	public static String get(String url) {
		HttpResult res = getResource(url);
		if(res.exception != null){
			throw new RuntimeException(res.exception);
		}
		else {
			return res.content;
		}
	}
	
	public static HttpResult getResource(String url) {
		HttpResult result = new HttpResult();

		try {
			HttpClient client = new DefaultHttpClient();
			HttpGet request = new HttpGet(url);
			HttpResponse response = client.execute(request);
			
			result.code = response.getStatusLine().getStatusCode();
			result.content = convertStreamToString(response.getEntity().getContent());
			
		} catch (Exception e) {
			result.exception = e;
		}

		return result;
	}
	

	public static void get(String url, HttpCallback callback) {
		HttpTask task = new HttpTask(url, callback);
		Thread t = new Thread(task);
		t.start();
	}

	public static void post(String url, Map<String, Object> data,
			HttpCallback callback) {
		HttpTask task = new HttpTask(url, callback);
		task.setPostData(data);
		Thread t = new Thread(task);
		t.start();
	}

	public static HttpResult post(String url, Map<String, Object> data) {
		return postWithProgress(url, data, null);
	}
	
	public static String postData(String url, Map<String, Object> data) {
		HttpResult res = postWithProgress(url, data, null);
		if(res.exception != null){
			throw new RuntimeException(res.exception);
		}
		else {
			return res.content;
		}
	}

	public static HttpResult postWithProgress(String url,
			Map<String, Object> data, ProgressListener listener){
		return postWithProgress(url, data, listener,true);
	}
	
	public static HttpResult postWithProgress(String url,
			Map<String, Object> data, ProgressListener listener, boolean stringifyresp) {
		HttpTask task = new HttpTask(url, null);
		task.setListener(listener);
		task.setPostData(data);
		task.run();
		int code = task.response_.getStatusLine().getStatusCode();

		HttpResult result = new HttpResult();
		result.code = code;

		if (task.exception_ != null) {
			task.exception_.printStackTrace();
			result.exception = task.exception_;
		} else if (code > 399 || code < 200) {
			result.content = null;
		} else if(stringifyresp){
			try {
				result.content = convertStreamToString(task.response_
						.getEntity().getContent());
			} catch (Exception e) {
				result.content = null;
				result.exception = e;
			}
		}
		return result;
	}
	
	public static String responseToString(HttpResponse response) throws IOException{
		InputStream in = response.getEntity().getContent();
		InputStreamReader ir = new InputStreamReader(in);
		BufferedReader bin = new BufferedReader(ir);
		String line = null;
		StringBuffer buff = new StringBuffer();
		while((line = bin.readLine())!=null){
			buff.append(line+"\n");
		}
		bin.close();
		return buff.toString();
	}
	
	public void doGet(String url, HttpCallback callback) {
		HttpGet get = new HttpGet(url);
		HttpRequestInfo rinfo = new HttpRequestInfo(get, callback);
		AsyncHttpTask task = new AsyncHttpTask();
		task.execute(rinfo);
	}

	public void doPost(String url, Map<String, String> params,
			HttpCallback callback) {
		try {

			HttpPost post = new HttpPost(url);
			List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(
					params.size());

			for (String key : params.keySet()) {
				nameValuePairs
						.add(new BasicNameValuePair(key, params.get(key)));
			}

			UrlEncodedFormEntity entity = new UrlEncodedFormEntity(
					nameValuePairs);
			post.setEntity(entity);

			HttpRequestInfo rinfo = new HttpRequestInfo(post, callback);
			AsyncHttpTask task = new AsyncHttpTask();
			task.execute(rinfo);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

}
